package com.hancher.gamelife2.backup;

import static com.hancher.common.net.download.DownloadHelper.writeFileFromIS;

import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;

import com.hancher.common.androidutil.AsyncUtils;
import com.hancher.common.androidutil.LogUtil;
import com.hancher.common.androidutil.MmkvUtil;
import com.hancher.common.javautil.EmptyUtil;
import com.hancher.common.javautil.GsonUtil;
import com.hancher.common.net.NetUtil;
import com.hancher.common.net.download.DownloadListener;
import com.hancher.gamelife2.login.BaiduLoginFragment;

import java.io.File;
import java.util.HashMap;
import java.util.List;

import io.reactivex.rxjava3.core.Observable;
import lombok.Data;
import lombok.Getter;
import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.HeaderMap;
import retrofit2.http.Headers;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;
import retrofit2.http.Streaming;
import retrofit2.http.Url;

/**
 * 作者: Hancher
 * 日期: 2020/9/6 10:21
 * 描述: 百度网盘接口
 */
public class BaiduNetApi {
    public static String BDPAN_BACKUP_PATH = "/apps/GameLife2/";
    public static String HOST = "https://pan.baidu.com/";
    public static ApiService service = new NetUtil<>(ApiService.class).setBaseUrl(HOST).setUseJump(true).setTimeOut(600).build();

    /**
     * 1. 预上传是通知网盘云端新建一个上传任务
     * method	string	是	precreate	URL参数	本接口固定为precreate
     * access_token	string	是	12.a6b7dbd428f731035f771b8d15063f61.86400.1292922000-2346678-124328	URL参数	接口鉴权认证参数，标识用户
     * path	string	是	/apps/appName/filename.jpg	RequestBody参数	上传后使用的文件绝对路径，需要urlencode
     * size	int	是	4096	RequestBody参数	文件和目录两种情况：上传文件时，表示文件的大小，单位B；上传目录时，表示目录的大小，目录的话大小默认为0
     * isdir	int	是	0	RequestBody参数	是否为目录，0 文件，1 目录
     * block_list	string	是	["98d02a0f54781a93e354b1fc85caf488", "ca5273571daefb8ea01a42bfa5d02220"]	RequestBody参数	文件各分片MD5数组的json串。block_list的含义如下，如果上传的文件小于4MB，其md5值（32位小写）即为block_list字符串数组的唯一元素；如果上传的文件大于4MB，需要将上传的文件按照4MB大小在本地切分成分片，不足4MB的分片自动成为最后一个分片，所有分片的md5值（32位小写）组成的字符串数组即为block_list。
     * @return
     */
    public static Observable<PreUploadVO> preUpload(PreUploadDTO resquest) {
        return service.preUpload(MmkvUtil.getString(BaiduLoginFragment.BAIDUNET_ACCESS_TOKEN), GsonUtil.obj2Map(resquest))
                .compose(AsyncUtils.getThreadTransformer());
    }

    /**
     * 2. 分片上传
     * @param resquest
     * @param file
     * @return
     */
    public static Observable<UploadVO> upload(UploadDTO resquest, File file) {
        HashMap<String, Object> map = GsonUtil.obj2Map(resquest);
        MultipartBody.Part filePart = NetUtil.fileParam2File("file", file);
        return service.upload(map, filePart)
                .compose(AsyncUtils.getThreadTransformer());
    }

    /**
     * 3. 上传完成
     * @param resquest
     * @return
     */
    public static Observable<UploadFinishVO> uploadFinish(UploadFinishDTO resquest) {
        return service.uploadFinish(MmkvUtil.getString(BaiduLoginFragment.BAIDUNET_ACCESS_TOKEN), GsonUtil.obj2Map(resquest))
                .compose(AsyncUtils.getThreadTransformer());
    }

    /**
     * 根据文件夹查询文件列表
     * @param resquest
     * @return
     */
    public static Observable<ListVO> list(ListDTO resquest) {
        return service.list(GsonUtil.obj2Map(resquest))
                .compose(AsyncUtils.getThreadTransformer());
    }

    /**
     * 根据文件fsid查询文件信息
     * @param resquest
     * @return
     */
    public static Observable<ListInfoVO> listInfo(ListInfoDTO resquest) {
        return service.listInfo(GsonUtil.obj2Map(resquest))
                .compose(AsyncUtils.getThreadTransformer());
    }

    /**
     * 下载文件
     * @param url
     * @param downPath
     * @param downloadListener
     */
    public static void download(String url, String downPath, final DownloadListener downloadListener) {
        Handler mainThread = new Handler(Looper.getMainLooper());
        service.download(url).enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(@androidx.annotation.NonNull Call<ResponseBody> call, @androidx.annotation.NonNull final Response<ResponseBody> response) {
                LogUtil.d("");
                String url2 = MmkvUtil.getString("jump_302");
                String mapStr = MmkvUtil.getString("jump_302_header");
                LogUtil.i(mapStr);
                HashMap<String, Object> map = GsonUtil.str2Map(mapStr);
                MmkvUtil.put("jump_302", "");
                if (EmptyUtil.isNotEmpty(url2)) {
                    HashMap<String, String> headers = new HashMap<>();
                    headers.put("Set-Cookie", map.get("Set-Cookie") + "");
                    service.downloadRedirt(url2, headers).enqueue(new Callback<ResponseBody>() {
                        @Override
                        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                            //将Response写入到从磁盘中，详见下面分析
                            //注意，这个方法是运行在子线程中的
                            LogUtil.d("");
                            ResponseBody body = response.body();
                            if (body == null) {
                                // 可能下载完成，数据为空
                                mainThread.post(() -> downloadListener.onFinish(downPath));
                                return;
                            }
                            writeFileFromIS(new File(downPath), body.byteStream(), body.contentLength(), mainThread, downloadListener);
                        }

                        @Override
                        public void onFailure(Call<ResponseBody> call, Throwable t2) {
                            LogUtil.d("");
                            mainThread.post(() -> downloadListener.onFail(t2.getMessage()));
                        }
                    });
                }
            }

            @Override
            public void onFailure(@androidx.annotation.NonNull Call<ResponseBody> call, @NonNull Throwable t3) {
                LogUtil.d("");
                mainThread.post(() -> downloadListener.onFail(t3.getMessage()));
            }
        });
    }

    interface ApiService {

        @Headers("Host: pan.baidu.com")
        @POST("rest/2.0/xpan/file?method=precreate")
        @FormUrlEncoded
        Observable<PreUploadVO> preUpload(@Query("access_token") String access_token, @FieldMap HashMap<String, Object> map);

        @Headers("Host: d.pcs.baidu.com")
        @Multipart
        @POST("rest/2.0/pcs/superfile2?method=upload&type=tmpfile")
        Observable<UploadVO> upload(@QueryMap HashMap<String, Object> map,
                                    @Part MultipartBody.Part file);

        @Headers("Host: pan.baidu.com")
        @POST("rest/2.0/xpan/file?method=create")
        @FormUrlEncoded
        Observable<UploadFinishVO> uploadFinish(@Query("access_token") String access_token, @FieldMap HashMap<String, Object> map);

        @Headers("Host: pan.baidu.com")
        @POST("rest/2.0/xpan/file?method=list&order=time&desc=1")
        Observable<ListVO> list(@QueryMap HashMap<String, Object> map);

        @Headers("Host: pan.baidu.com")
        @GET("rest/2.0/xpan/multimedia?method=filemetas&dlink=1")
        Observable<ListInfoVO> listInfo(@QueryMap HashMap<String, Object> map);

        @GET
        @Headers({"Host:d.pcs.baidu.com", "User-Agent:pan.baidu.com"})
        Call<ResponseBody> download(@Url String url);

        @Streaming
        @GET
        Call<ResponseBody> downloadRedirt(@Url String url, @HeaderMap HashMap<String, String> headers);
    }

    public static class PreUploadDTO {
        @Getter
        String path;
        @Getter
        String block_list;
        @Getter
        Long size;
        int autoinit = 1;
        @Getter
        int isdir = 0;

        public PreUploadDTO setIsdir(int isdir) {
            this.isdir = isdir;
            return this;
        }

        public PreUploadDTO setPath(String path) {
            this.path = path;
            return this;
        }

        public PreUploadDTO setBlock_list(String block_list) {
            this.block_list = block_list;
            return this;
        }

        public PreUploadDTO setSize(Long size) {
            this.size = size;
            return this;
        }

        public Integer getAutoinit() {
            return autoinit;
        }

        public PreUploadDTO setAutoinit(Integer autoinit) {
            this.autoinit = autoinit;
            return this;
        }
    }

    @Data
    public static class PreUploadVO {
        Integer errno;
        String path;
        String uploadid;
        Integer return_type;
        List<Integer> block_list;

        public Integer getErrno() {
            if (errno == null) {
                return 0;
            }
            return errno;
        }
    }

    @Getter
    public static class UploadDTO {
        String access_token;
        String path;
        String uploadid;
        String partseq;


        public UploadDTO setAccess_token(String access_token) {
            this.access_token = access_token;
            return this;
        }

        public UploadDTO setPath(String path) {
            this.path = path;
            return this;
        }

        public UploadDTO setUploadid(String uploadid) {
            this.uploadid = uploadid;
            return this;
        }

        public UploadDTO setPartseq(String partseq) {
            this.partseq = partseq;
            return this;
        }
    }

    @Data
    public static class UploadVO {
        Integer errno;
        String md5;

        public Integer getErrno() {
            if (errno == null) {
                return 0;
            }
            return errno;
        }
    }

    @Getter
    public static class UploadFinishDTO {
        String path;
        Long size;
        Integer isdir;
        String block_list;
        String uploadid;
        String rtype;

        public UploadFinishDTO setRtype(String rtype) {
            this.rtype = rtype;
            return this;
        }

        public UploadFinishDTO setPath(String path) {
            this.path = path;
            return this;
        }

        public UploadFinishDTO setSize(Long size) {
            this.size = size;
            return this;
        }

        public UploadFinishDTO setIsdir(Integer isdir) {
            this.isdir = isdir;
            return this;
        }

        public UploadFinishDTO setBlock_list(String block_list) {
            this.block_list = block_list;
            return this;
        }

        public UploadFinishDTO setUploadid(String uploadid) {
            this.uploadid = uploadid;
            return this;
        }
    }

    @Data
    public static class UploadFinishVO {
        Integer errno;
        String md5;
        String fs_id;
        String server_filename;
        Integer category;
        Integer isdir;
        String path;
        Long size;
        Long ctime;
        Long mtime;

        public Integer getErrno() {
            if (errno == null) {
                return 0;
            }
            return errno;
        }
    }

    @Getter
    public static class ListDTO {
        String access_token;
        String dir;
        int web = 1;

        public ListDTO setAccess_token(String access_token) {
            this.access_token = access_token;
            return this;
        }

        public ListDTO setDir(String dir) {
            this.dir = dir;
            return this;
        }
    }

    @Data
    public static class ListVO {

        Integer errno;
        String guid_info;
        List<Item> list;

        public Integer getErrno() {
            if (errno == null) {
                return 0;
            }
            return errno;
        }

        @Data
        public static class Item {
            String fs_id;
            String path;
            String server_filename;
            Long size;
            Long server_mtime;
            Long server_ctime;
            Long local_mtime;
            Long local_ctime;
            Integer isdir;
            Integer category;
            String md5;
            Integer dir_empty;
            Thumb thumbs;

            @Data
            public static class Thumb {
                String icon;
                String url1;
                String url2;
                String url3;
                String url4;
            }
        }
    }

    @Getter
    public static class ListInfoDTO {
        String access_token;
        List<String> fsids;
        String thumb = "1";
        String extra = "1";
        String dlink = "1";

        public ListInfoDTO setAccess_token(String access_token) {
            this.access_token = access_token;
            return this;
        }

        public ListInfoDTO setFsids(List<String> fsids) {
            this.fsids = fsids;
            return this;
        }
    }

    @Data
    public static class ListInfoVO {

        Integer errno;
        String errmsg;
        String request_id;
        List<Item> list;

        public Integer getErrno() {
            if (errno == null) {
                return 0;
            }
            return errno;
        }

        @Data
        public static class Item {

            Integer category;
            Long date_taken;
            String dlink;
            String filename;
            String fs_id;
            Integer height;
            Integer width;
            Integer isdir;
            String md5;
            Long oper_id;
            String path;
            Long server_mtime;
            Long server_ctime;
            Long size;
            Thumb thumbs;

            @Data
            public static class Thumb {
                String url1;
                String url2;
                String url3;
                String url4;
                String icon;

            }
        }

    }
}
